The Content Model is a way of factoring part data so that the part-specific code for internalizing and externalizing data is gathered in one class hierarchy. Every part with persistent data should define a subclass of FW_CContent (or FW_CEmbeddingContent). This content object holds the part’s persistent data, and provides Internalize and Externalize methods for reading and writing that data to a storage unit.
In previous releases of ODF (and in parts written directly in OpenDoc), the part object contained all the data. Methods for in/externalizing the data were defined for both the part class and the selection class, so that often the same or similar code was duplicated across several classes (e.g. FW_CPart::ExternalizeContent, FW_CSelection::DoExternalizeSelection, FW_CSelection:: ExternalizeData). In ODF 1 the user can define a hierarchy of content classes so that code that deals with one kind of data can be shared as needed.
In addition to managing part data, a content object is used to designate a subset of part data in a selection, promise, or link. If the part supports data interchange operations like clipboard commands or drag and drop, it must define a selection class, which should have an associated content object for designating the selected data. Depending on what kind of data the part manages, the selection’s content class may hold the data itself (e.g. ODFHello, ODFEmbed) or point to a subset of the part’s data (e.g. ODFDraw, ODFContainer).
Whenever a data interchange operation is initiated (cut, copy, paste, drag, drop), the selection’s content object is called upon to read or write its data. The selection object’s content class must override a small number of methods that perform in/externalization of the selected data.
Examples:
- Hello part’s “selection” is the text displayed by the part. The selection object doesn’t have its own content object, but simply refers to that of the part.
- Table part’s data consists of a list of embedded parts (proxies), each of which is located in a cell of the table. A selection consists of a single cell, which may or may not contain a proxy. The selected cell is indicated by the cell’s coordinates.
- Draw part’s data consists of a list of shapes. The selection’s content object keeps its own list of shape pointers.
See also the section “Defining your Part’s Content Object” on p.45 of the ODF Developer’s Guide.
Implementing the content model
The amount of work that needs to be done to implement the content model in a part depends on whether the part supports embedding or data interchange. In addition to the basic content methods Externalize and Internalize, embedding parts must override two or three methods to handle the single-embedded-frame case. Parts that support data interchange must define a selection object and its associated content object. Additional content classes may be needed to support promises and linking.
The following is an outline of the basic steps for adding a content model to an ODF part. Part types are divided into four cases, going from simple to complex.
Case 1) Non-embedding part, no data interchange
Example: Clock
Subclass FW_CContent (holds part data)
Override FW_CContent::Externalize and FW_CContent::Internalize
Override FW_CPart::NewPartContent (creates and returns a content object)
The part should keep a pointer to its content object, in order to access its data.
Override FW_CContent method Internalize (reads data into the selection)
Override FW_CEmbeddingContent method IsDataOnlyOneProxy (tells if selected data is a single proxy)
Override FW_CEmbeddingContent method SingleEmbeddedFrameInternalized (adds newly embedded part to selection’s content)
In your override of FW_CSelection::GetSelectedContent, return the Content object for the selection, not the part
Who Calls What? And When?
Below are some diagrams intended to illustrate how user-supplied methods fit into the ODF content model flow of control. This interaction diagram notation is adapted from that presented in the book Design Patterns, by Gamma, Helm, Johnson, and Vlissides. The time line flows from top to bottom. A rectangle represents an object that is executing a request, and a vertical line indicates the lifetime of an object. Horizontal arrows denote requests sent to other objects. Functions that are provided by ODF are white rectangles, functions that must be supplied by the part writer are filled, and functions that may be overridden are striped.
Diagram 1 shows the flow of execution when OpenDoc tells a part to Externalize its data. The part itself only needs to override the Externalize method of its Content class. If necessary the part may also override its AddProperties method.
Diagram 1
Diagram 2 shows the flow of execution when OpenDoc tells a part to internalize its data from a storage unit at part initialization time. Only the Internalize method of the part’s Content class must be overridden. The part may also override the Initialize method to perform part-specific initialization.
Diagram 2
Diagram 3 shows what happens when selected data is externalized, as in a Copy or Drag command. To externalize a selection, you need to override at least the Externalize method of your selection’s Content class:
Diagram 3
Diagram 4 shows what happens when data is internalized into a selection object, as in a Paste or Drop command. To internalize a selection, you need to override the Internalize method of your selection’s Content class:
Diagram 4
The above diagrams illustrate simple non-embedding parts. Things get a little more complicated with parts that support embedding, because they have to override some additional methods to deal with the single-embedded-frame case. Diagram 5 shows some (not all!) of the steps involved in internalizing a single embedded frame.
Diagram 5
When internalizing a single embedded frame, ODF first clones the frame/part, and if successful calls the Content method SingleEmbeddedFrameInternalized. This method must be overridden by embedding parts to take care of integrating the new embedded part into part data.